package com.alameda.common.config.mybatis;

import com.alameda.common.utils.ThreadLocalUtils;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * mybatis 自动填充拦截器
 *
 * @author Lv
 * @date 2024/4/8
 */
@Slf4j
@Component
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class MybatisAutoFillValueInterceptor implements Interceptor {
    /**
     * 拦截sql操作
     *
     * @param invocation 调用对象
     * @return
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        // 获取sql执行类型：insert、update、select、delete
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();

        if (SqlCommandType.INSERT.equals(sqlCommandType)) {
            Object param = getParam(invocation, sqlCommandType);
            if (param == null) {
                // sql脚本执行场景 不做处理
                return invocation.proceed();
            }
            Field[] fields = getAllFields(param);

            for (Field field : fields) {
                // 设定创建人id
                if ("createId".equals(field.getName())) {
                    field.setAccessible(Boolean.TRUE);
                    field.set(param, ThreadLocalUtils.getUserId());
                    field.setAccessible(Boolean.FALSE);
                }

                // 设置修改时间
                if ("createTime".equals(field.getName())) {
                    field.setAccessible(Boolean.TRUE);
                    field.set(param, LocalDateTime.now());
                    field.setAccessible(Boolean.FALSE);
                }

            }

        } else if (SqlCommandType.UPDATE.equals(sqlCommandType)) {
            Object param = getParam(invocation, sqlCommandType);
            if (param == null) {
                // sql脚本执行场景 不做处理
                return invocation.proceed();
            }
            Field[] fields = getAllFields(param);

            for (Field field : fields) {
                // 设定修改人id
                if ("updateId".equals(field.getName())) {
                    field.setAccessible(Boolean.TRUE);
                    field.set(param, ThreadLocalUtils.getUserId());
                    field.setAccessible(Boolean.FALSE);
                }

                // 设置修改时间
                if ("updateTime".equals(field.getName())) {
                    field.setAccessible(Boolean.TRUE);
                    field.set(param, LocalDateTime.now());
                    field.setAccessible(Boolean.FALSE);
                }
            }
        }

        return invocation.proceed();
    }

    /**
     * 获取参数
     *
     * @param invocation     调用对象
     * @param sqlCommandType sql类型
     * @return
     */
    @SuppressWarnings("all")
    private Object getParam(Invocation invocation, SqlCommandType sqlCommandType) {
        Object param = null;
        if (SqlCommandType.INSERT.equals(sqlCommandType)) {
            param = invocation.getArgs()[1];
        } else {
            try {
                Map<String, Object> parameter = (Map<String, Object>) invocation.getArgs()[1];
                param = parameter.get("param1");
            } catch (Exception exception) {
                try {
                    // 处理mybatis-plus逻辑删除无法获取参数问题 采用反射方式
                    Class<?> aClass = invocation.getArgs()[1].getClass();
                    TableName annotation = aClass.getAnnotation(TableName.class);
                    param = annotation.value();
                } catch (Exception e) {
                    // sql脚本执行场景
                    return null;
                }
            }
        }
        return param;
    }

    /**
     * 获取类的所有属性，包括父类
     */
    private Field[] getAllFields(Object object) {
        Class<?> clazz = object.getClass();
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        Field[] fields = new Field[fieldList.size()];
        fieldList.toArray(fields);
        return fields;
    }
}
